
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include "StringUtils.h"
#include "Prompt.h"

/** Constructor for the prompting class.
 * 
 * @param answer
 * This is the answer to be given when this object is queried. This
 * overrides the behaviour of this object, so generate any non-trivial
 * behaviour, this must be NULL.
 * 
 * @param maxreplylength
 * The maximum allowable reply length. */
Prompt::Prompt(const char *answer, int maxreplylength)
{
   anscopy = (answer) ? strdup(answer) : NULL;
   ansmaxlength = maxreplylength;
}

/** Copy constructor for the class.
 * 
 * @param o
 * The class to copy from. */
Prompt::Prompt(const Prompt &o)
{
   copy(o);
}

/** Copy assignment operator for the class.
 * 
 * @param o
 * The class to copy from.
 * 
 * @return
 * Itself. */
Prompt &Prompt::operator=(const Prompt &o)
{
   copy(o);
   return *this;
}

/** Destructor for the prompting class, destroying the default or
 * saved answer. */
Prompt::~Prompt()
{
   StringUtils::razeString(&anscopy);
}

/** Copy function for the prompt.
 * 
 * @param o
 * The class to copy from. */
void Prompt::copy(const Prompt &o)
{
   anscopy = (o.anscopy) ? strdup(o.anscopy) : NULL;
}

/** A variadic prompting function for yes/no questions.
 * 
 * @param promptstring
 * A format string with which to prompt the user.
 * 
 * @return
 * 1 for yes, 0 for no (default to zero). */
int Prompt::promptYesNoQuestion(const char *promptstring, ...)
{
   va_list args;
   va_list newcopy;
   va_start(args, promptstring); va_copy(newcopy, args);
   int tret = promptYesNoQuestion(promptstring, newcopy);
   va_end(newcopy); va_end(args);
   return tret;
}

/** A variadic prompting function for passwords.
 * 
 * @param promptstring
 * A format string with which to prompt the user for unprinted text.
 * 
 * @return
 * The unprinted string. */
const char *Prompt::promptForPassword(const char *promptstring, ...)
{
   va_list args;
   va_list newcopy;
   va_start(args, promptstring); va_copy(newcopy, args);
   const char *tret = promptForPassword(promptstring, newcopy);
   va_end(newcopy); va_end(args);
   return tret;
}

/** A variadic prompting function for yes/no questions.
 * 
 * @param promptstring
 * A format string to prompt the user with.
 * 
 * @param args
 * The variadic arguments list.
 * 
 * @return
 * 1 for yes, 0 for no (default to zero). */
int Prompt::promptYesNoQuestion(const char *promptstring, va_list args)
{
   char *prompt = StringUtils::varprintf(promptstring, args);
   char *result = NULL;
   while (1)
   {
      int retval = -1;
      if (anscopy)
      {
         result = (char *)anscopy;
         anscopy = NULL;
      }
      else
         result = promptNormal(prompt);
      if (result == NULL) retval = 0; // ASSUMING THAT NO IS THE SAFE OPTION!
      if (strcmp(result, "Yes") == 0) retval = 1;
      if (strcmp(result, "YES") == 0) retval = 1;
      if (strcmp(result, "yes") == 0) retval = 1;
      if (strcmp(result, "No") == 0) retval = 0;
      if (strcmp(result, "NO") == 0) retval = 0;
      if (strcmp(result, "no") == 0) retval = 0;
      if (retval != -1)
      {
         anscopy = result;
         free(prompt);
         return retval;
      }
      free(result);
      printf("Please type 'yes' or 'no'.\n");
   }
}

/** A variadic prompting function for passwords.
 * 
 * @param promptstring
 * A format string with which to prompt the user for unprinted text.
 * 
 * @param args
 * The variadic arguments list.
 * 
 * @return
 * The unprinted string. */
const char *Prompt::promptForPassword(const char *promptstring, va_list args)
{
   const char *prompt = StringUtils::varprintf(promptstring, args);
   anscopy = promptPassword(prompt);
   return anscopy;
}

/** Shows the given prompt and collects a response. The response must be freed
 * by the caller.
 * 
 * @param prompt
 * Prompt to show.
 * 
 * @return
 * The text typed by the user. This must be eventually freed by the caller. */
char *Prompt::promptNormal(const char *prompt)
{
   char *password = (char *)malloc(sizeof(char)*(ansmaxlength+1+1));
   char *p;

   if ((p = fgets(password, ansmaxlength+1, stdin)) != NULL)
   {
      size_t finalc = strlen(password);
      // Remove trailing control characters
      // (non-printable - reasonable assumption that it's whitespace)
      while (password[--finalc] < ((char)0x20))
         password[finalc] = '\0';
   } else
   {
      free(password);
      password = NULL;
   }
   return password;
}

/** Shows the given prompt and collects a secret response. The response must
 * be freed (and preferentially destroyed with prejudice) by the caller.
 * 
 * @param prompt
 * Prompt to show.
 * 
 * @return
 * The secret text typed by the user. This must be eventually freed by the
 * caller. */
char *Prompt::promptPassword(const char *prompt)
{
   char *password = (char *)malloc(sizeof(char)*(ansmaxlength+1+1));
   struct termios con_old, con_new;
   printf("%s", prompt);
   if (tcgetattr(fileno(stdin), &con_old) != 0)
      return NULL;
   con_new = con_old;
   con_new.c_lflag &= ~ECHO;
   if (tcsetattr(fileno(stdin), TCSAFLUSH, &con_new) != 0)
      return NULL;
   char *p;
   if ((p = fgets(password, ansmaxlength+1, stdin)) != NULL)
   {
      size_t finalc = strlen(password);
      // Remove trailing control characters
      // (non-printable - reasonable assumption that it's whitespace)
      while (password[--finalc] < ((char)0x20))
         password[finalc] = '\0';
   } else
   {
      free(password);
      password = NULL;
   }
   tcsetattr(fileno(stdin), TCSAFLUSH, &con_old);
   printf("\n");
   return password;
}
